import { Node, TiledMap, TiledMapAsset, UITransform, v2, v3, Vec2, Vec3 } from 'cc';
import { app } from 'db://app-game/core/app';
import { ViewUtil } from 'db://app-game/core/utils/ViewUtil';
import {
  ColliderParams,
  MonitorGroupComp,
} from 'db://app-game/libs/ecs/common/model/MonitorGroupComp';
import { ecs } from 'db://app-game/libs/ecs/ECS';
import { GamePrefab } from '../../../common/config/GamePrefab';
import { GameResPath } from '../../../common/config/GameResPath';
import { MapBaseInfo } from './LevelMap.type';
import { LevelMapModelComp } from './model/LevelMapModelComp';
import { LevelMapModelRenderComp } from './model/LevelMapRenderModelComp';
import { GridNode } from 'db://app-game/libs/math/graph/Graph';

/** 关卡地图 */
@ecs.register('LevelMap')
export class LevelMap extends ecs.Entity {
  // 数据层
  LevelMapModel!: LevelMapModelComp;

  // 业务层
  LevelMapModelRender!: LevelMapModelRenderComp; // 渲染模型组件
  MonitorGroup!: MonitorGroupComp; // 监视器组件

  // 视图层
  /** 地图节点 */
  mapNode!: Node;
  /** 怪兽工厂节点 */
  monsterFactoryNode!: Node;
  /** 角色工厂节点 */
  roleFactoryNode!: Node;
  /** 静态生物工厂节点 */
  staticCreatureFactoryNode!: Node;
  /** 特效工厂节点 */
  effectFactoryNode!: Node;
  init() {
    this.addComponents(LevelMapModelComp);
  }

  /** 加载地图节点 */
  load() {
    // 1.创建地图节点
    this.createNode();

    // 2.添加监视器组件
    this.addComponents(MonitorGroupComp);
    this.MonitorGroup.group = [
      {
        name: this.staticCreatureFactoryNode.name, // 静态生物工厂节点名
        monitorNode: this.staticCreatureFactoryNode,
      },
      {
        name: this.monsterFactoryNode.name, // 怪兽工厂节点名
        monitorNode: this.monsterFactoryNode,
      },
    ];
  }

  /** 创建地面子节点 */
  createNode() {
    // 1.创建地图节点
    this.mapNode = new Node();
    this.mapNode.name = 'map';
    this.mapNode.addComponent(UITransform);
    this.mapNode.parent = app.gui.game;

    // 2.添加怪兽工厂节点
    this.monsterFactoryNode = new Node();
    this.monsterFactoryNode.name = 'monster-factory';
    this.monsterFactoryNode.addComponent(UITransform);
    this.monsterFactoryNode.parent = app.gui.game;

    // 3.添加特效工厂节点
    this.effectFactoryNode = new Node();
    this.effectFactoryNode.name = 'effect-factory';
    this.effectFactoryNode.addComponent(UITransform);
    this.effectFactoryNode.parent = app.gui.game;

    // 4.添加角色工厂节点
    this.roleFactoryNode = new Node();
    this.roleFactoryNode.name = 'role-factory';
    this.roleFactoryNode.addComponent(UITransform);
    this.roleFactoryNode.parent = app.gui.game;

    // 5.添加静态生物工厂节点
    this.staticCreatureFactoryNode = new Node();
    this.staticCreatureFactoryNode.name = 'static-creature-factory';
    this.staticCreatureFactoryNode.addComponent(UITransform);
    this.staticCreatureFactoryNode.parent = app.gui.game;
  }

  /** 设置渲染跟随目标 */
  setfollowTarget(target: Node) {
    this.add(LevelMapModelRenderComp);
    this.LevelMapModelRender.target = target;
  }

  /** 创建关卡地图节点 */
  async createMapNode(row: number, col: number) {
    const mapName = this.LevelMapModel.mapTable[row][col];
    const mapFileName = this.LevelMapModel.mapFileName;
    const mapPath = GameResPath.getLevelMapPath(mapFileName, mapName);
    const node = ViewUtil.createPrefabNode(GamePrefab.LevelMap);
    const tmxAsset = await app.res.loadAsync(mapPath, TiledMapAsset);
    const tileMap = node.addComponent(TiledMap);
    tileMap.tmxAsset = tmxAsset;
    return node;
  }

  /** 获取地图信息 */
  getMapInfo(mapIndex: { row: number; col: number }): MapBaseInfo {
    const lmmr = this.LevelMapModelRender;
    const { row, col } = mapIndex;
    if (lmmr.currentMap.has(`${row}-${col}`)) {
      return lmmr.currentMap.get(`${row}-${col}`);
    }
    const { chunkMapWidth, chunkMapHeight, initCol, initRow } = this.LevelMapModel;
    const mapX = (col - initCol) * chunkMapWidth;
    const mapY = -(row - initRow) * chunkMapHeight;
    const mapInfo = {
      id: `${row}-${col}`,
      pos: v2(mapX, mapY),
      row,
      col,
      minX: mapX - chunkMapWidth / 2,
      maxX: mapX + chunkMapWidth / 2,
      minY: mapY - chunkMapHeight / 2,
      maxY: mapY + chunkMapHeight / 2,
      rendering: false,
    };
    return mapInfo;
  }

  /** 计算地图索引位置 */
  computedMapIndex(pos: Vec3, velocity: Vec3): { row: number; col: number; mapState: boolean } {
    const { chunkMapWidth, chunkMapHeight, maxRow, maxCol } = this.LevelMapModel;
    const newPos = v2(
      pos.x + velocity.x + chunkMapWidth / 2,
      pos.y + velocity.y + chunkMapHeight / 2
    );
    const offsetRow = -Math.floor(newPos.y / chunkMapHeight);
    const offsetCol = Math.floor(newPos.x / chunkMapWidth);

    const row = this.LevelMapModel.initRow + offsetRow;
    const col = this.LevelMapModel.initCol + offsetCol;

    if (row >= 0 && row <= maxRow && col >= 0 && col <= maxCol) {
      return { row, col, mapState: true };
    } else {
      return { row, col, mapState: false };
    }
  }

  /** 地图边界拦截 */
  mapBoundaryInterception(pos: Vec3, velocity: Vec3) {
    const { chunkMapWidth, chunkMapHeight } = this.LevelMapModel;
    const { row, col } = this.computedMapIndex(pos, velocity);
    const currentMap = this.LevelMapModelRender.currentMap;

    // 前进地图x和y都没超出地图
    if (currentMap.has(`${row}-${col}`)) {
      return {
        mapInfo: currentMap.get(`${row}-${col}`),
        velocity: velocity,
      };
    }

    // 计算以前没超出时候的位置
    const oldPos = v2(pos.x + chunkMapWidth / 2, pos.y + chunkMapHeight / 2);
    const oldOffsetRow = -Math.floor(oldPos.y / chunkMapHeight);
    const oldOffsetCol = Math.floor(oldPos.x / chunkMapWidth);

    const oldRow = this.LevelMapModel.initRow + oldOffsetRow;
    const oldCol = this.LevelMapModel.initCol + oldOffsetCol;

    // 移动速度
    let x = velocity.x;
    let y = velocity.y;

    // 检查x轴是否超出
    if (currentMap.has(`${row}-${oldCol}`)) {
      const mapInfo = currentMap.get(`${row}-${oldCol}`);
      if (velocity.x < 0) {
        x = mapInfo.minX - oldPos.x + chunkMapWidth / 2 + 2;
      } else {
        x = mapInfo.maxX - oldPos.x + chunkMapWidth / 2 - 2;
      }
      return {
        mapInfo,
        velocity: v3(x, y, 0),
      };
    }

    // 检查y抽是否超出
    if (currentMap.has(`${oldRow}-${col}`)) {
      const mapInfo = currentMap.get(`${oldRow}-${col}`);
      if (velocity.y < 0) {
        y = mapInfo.minY - oldPos.y + chunkMapHeight / 2 + 2;
      } else {
        y = mapInfo.maxY - oldPos.y + chunkMapHeight / 2 - 2;
      }
      return {
        mapInfo,
        velocity: v3(x, y, 0),
      };
    }

    // x,y都超了，
    const mapInfo = currentMap.get(`${oldRow}-${oldCol}`);
    if (velocity.x < 0) {
      x = mapInfo.minX - oldPos.x + chunkMapWidth / 2 + 2;
    } else {
      x = mapInfo.maxX - oldPos.x + chunkMapWidth / 2 - 2;
    }
    if (velocity.y < 0) {
      y = mapInfo.minY - oldPos.y + chunkMapHeight / 2 + 2;
    } else {
      y = mapInfo.maxY - oldPos.y + chunkMapHeight / 2 - 2;
    }
    return {
      mapInfo,
      velocity: v3(x, y, 0),
    };
  }

  /** 地图障碍物碰撞 */
  mapTiledCollision(pos: Vec3, velocity: Vec3, mapInfo: MapBaseInfo) {
    const newPos = v2(pos.x + velocity.x, pos.y + velocity.y);
    const { tileWidth, tileHeight } = this.LevelMapModel;
    const { minX, maxY } = mapInfo;
    const relativeY = maxY - newPos.y;
    const relativeX = newPos.x - minX;

    // 移动速度
    let x = velocity.x;
    let y = velocity.y;
    const oldRelativeY = maxY - pos.y;
    const oldRelativeX = pos.x - minX;
    const oldRow = Math.floor(oldRelativeY / tileHeight);
    const oldCol = Math.floor(oldRelativeX / tileWidth);

    const row = Math.floor(relativeY / tileHeight);
    const col = Math.floor(relativeX / tileWidth);
    const offsetVal = 4;

    // 检查x轴是否超出
    if (this.LevelMapModelRender.obstacleMap.has(`${mapInfo.id}`)) {
      const obstaclesMap = this.LevelMapModelRender.obstacleMap.get(`${mapInfo.id}`);
      if (obstaclesMap.has(`${col}-${oldRow}`)) {
        if (x < 0) {
          x = x - (relativeX - tileWidth * (col + 1)) + offsetVal;
        } else {
          x = x - (relativeX - tileWidth * col) - offsetVal;
        }
      }
    }

    // 检查y轴是否超出
    if (this.LevelMapModelRender.obstacleMap.has(`${mapInfo.id}`)) {
      const obstaclesMap = this.LevelMapModelRender.obstacleMap.get(`${mapInfo.id}`);
      if (obstaclesMap.has(`${oldCol}-${row}`)) {
        if (y < 0) {
          y = y - (tileHeight * row - relativeY) + offsetVal;
        } else {
          y = y - (tileHeight * row + tileHeight - relativeY) - offsetVal;
        }
      }
    }
    return {
      obstacle: null,
      velocity: v3(x, y, 0),
    };
  }

  /** 重置速度 */
  resetVelocity(pos: Vec3, velocity: Vec3) {
    let v = velocity;
    // 1.把位置更改到地图上面的位置
    const nPos = this.mapNode.getComponent(UITransform).convertToNodeSpaceAR(pos);

    // 3.计算出你在那块地图上
    const { mapInfo, velocity: newVelocity } = this.mapBoundaryInterception(nPos, v);
    // 3.1更新移动速度
    v = newVelocity;
    const { velocity: obstacleVelocity } = this.mapTiledCollision(nPos, v, mapInfo);
    v = obstacleVelocity;
    return v;
  }

  /**
   * 获取tiledMap位置
   * @param pos 角色位置
   */
  getTiledMapPos(pos: Vec3): Vec2 | null {
    const { tileColNumber, tileRowNumber, tileWidth, tileHeight } = this.LevelMapModel;

    const mapIndex = this.computedMapIndex(pos, v3(0, 0, 0));
    const mapInfo = this.getMapInfo(mapIndex);
    if (!mapInfo) return null;
    const { minX, maxY } = mapInfo;
    const relativeX =
      Math.floor((pos.x - minX) / tileWidth) + Math.floor(mapIndex.col * tileColNumber);
    const relativeY =
      Math.floor((maxY - pos.y) / tileHeight) + +Math.floor(mapIndex.row * tileRowNumber);

    return new Vec2(relativeX, relativeY);
  }

  /** 画布节点 */
  getGraphNode(pos: Vec3): GridNode | null {
    const tiledPos = this.getTiledMapPos(pos);
    return this.LevelMapModelRender.graph.getNode(tiledPos.x, tiledPos.y);
  }

  /** 瓦片位置转中心位置 */
  tiledPosToCenterPos(pos: Vec2): Vec3 {
    const { tileColNumber, tileRowNumber, tileWidth, tileHeight } = this.LevelMapModel;
    const col = Math.floor(pos.x / tileColNumber);
    const row = Math.floor(pos.y / tileRowNumber);
    const mapInfo = this.getMapInfo({ col, row });
    const centerPos = v3(mapInfo.minX + tileWidth * pos.x, mapInfo.maxY - tileHeight * pos.y, 0);
    return centerPos;
  }

  /** 碰撞体列表 */
  colliderList(res: ColliderParams) {
    const { quadTreeObj } = this.MonitorGroup;
    const { name, circle } = res;
    if (name && quadTreeObj[name]) {
      return quadTreeObj[name]?.retrieve(circle) || [];
    }
    return [];
  }
}
